package com.example.common.scan;

import com.example.bean.MsgHandlerMapping;
import com.example.common.annotation.Handler;
import com.example.common.annotation.HandlerMapping;
import com.example.common.converter.MsgTypeParser;
import com.example.common.data.msg.MsgType;
import com.example.common.util.ClassScanner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.TypeVariable;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class HandlerScanner implements InitializingBean , ApplicationContextAware {

    private final String packagesToScan;

    private final MsgTypeParser msgTypeParser;
    private final MsgHandlerMapping msgHandlerMapping;
    private  ApplicationContext applicationContext;

    public HandlerScanner(String packagesToScan,MsgHandlerMapping msgHandlerMapping,MsgTypeParser msgTypeParser) {
        this.packagesToScan = packagesToScan;
        this.msgHandlerMapping = msgHandlerMapping;
        this.msgTypeParser = msgTypeParser;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitializingBean.afterPropertiesSet");
        // 通过普通注解扫描 [MsgHandler], 不会加入Spring容器
//        detectMsgHandlerByAnnotation();
        // 从Spring容器中获取 [MsgHandler], 针对于受Spring管理的 [MsgHandler]
        detectMsgHandlerFromSpringContainer();
    }

    private void detectMsgHandlerByAnnotation() throws IOException, IllegalAccessException, InstantiationException {
        if (StringUtils.isEmpty(packagesToScan)) {
            log.info("[jt808.handler-scan.base-packages] is empty. Skip...");
            return;
        }
        final ClassScanner scanner = new ClassScanner();
        final Set<Class> handlerClassList = scanner.doScan(packagesToScan, this::isHandlerClass);
        if (!CollectionUtils.isEmpty(handlerClassList)) {
            detectMsgHandler(handlerClassList);
        }
    }

    private void detectMsgHandlerFromSpringContainer() throws IOException, InstantiationException, IllegalAccessException {
        @SuppressWarnings("rawtypes") final Set<Class> handlerClassListFromSpringContainer = applicationContext
                .getBeansWithAnnotation(Handler.class)
                .values().stream().map(Object::getClass).collect(Collectors.toSet());
        if (!handlerClassListFromSpringContainer.isEmpty()) {
            detectMsgHandler(handlerClassListFromSpringContainer);
        }
    }
    public void detectMsgHandler(Set<Class> handlerClassList) throws IOException, InstantiationException,IllegalAccessException {
        for (Class<?> cls : handlerClassList) {
            final Handler handlerAnnotation = AnnotationUtils.findAnnotation(cls, Handler.class);
            assert handlerAnnotation != null;
//            if (AbstractMsgHandler.class.isAssignableFrom(cls)) {
//                Optional<MsgType> optionalMsgType = msgTypeParser.parseMsgType(handlerAnnotation.msgType());
//                if (optionalMsgType.isPresent()) {
//                    msgHandlerMapping.registerHandler(optionalMsgType.get(), (MsgHandler<? extends RequestMsgBody>) createBeanInstance(cls));
//                    continue;
//                }
//            }
            final Method[] declaredMethods = ReflectionUtils.getAllDeclaredMethods(ClassUtils.getUserClass(cls));
            for (Method method : declaredMethods) {
                if (!isRequestMsgMappingMethod(method)) {
                    continue;
                }
                final HandlerMapping mappingAnnotation = method.getAnnotation(HandlerMapping.class);
                for (int msgId : mappingAnnotation.msgType()) {
                    MsgType msgType = msgTypeParser.parseMsgType(msgId).orElseThrow(() -> new RuntimeException("msgId:"+msgId));
                    msgHandlerMapping.inject(msgType, method);
                }
            }
        }

    }
    private boolean isHandlerClass(Class<?> cls) {
        return AnnotationUtils.findAnnotation(cls, Handler.class) != null;
    }
    private boolean isRequestMsgMappingMethod(Method method) {
        return method.getAnnotation(HandlerMapping.class) != null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
